{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `LSTM` for MNIST digits classification\n",
    "\n",
    "In this example, everything is the same as the `SimpleRNN` for MNIST digits classification. Only one part is changed. Instead of using a `SimpleRNN` layer, we used a `LSTM`.  We can achieve `~96.2%` test accuracy after `20 epochs`. This is lower compared to `SimpleRNN`. Why? If the optimizer is changed to `adam`, the accuracy jumps to `~98.9%` while doing the same change on `SimpleRNN` will only get us to about `~98.0%`.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"LSTM_MNIST\"\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "lstm_3 (LSTM)                (None, 256)               291840    \n",
      "_________________________________________________________________\n",
      "dense_3 (Dense)              (None, 10)                2570      \n",
      "=================================================================\n",
      "Total params: 294,410\n",
      "Trainable params: 294,410\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "Epoch 1/20\n",
      "469/469 [==============================] - 59s 125ms/step - loss: 2.2275 - accuracy: 0.2495\n",
      "Epoch 2/20\n",
      "469/469 [==============================] - 61s 131ms/step - loss: 1.8862 - accuracy: 0.3731\n",
      "Epoch 3/20\n",
      "469/469 [==============================] - 68s 145ms/step - loss: 1.4784 - accuracy: 0.5159\n",
      "Epoch 4/20\n",
      "469/469 [==============================] - 65s 138ms/step - loss: 1.1222 - accuracy: 0.6288\n",
      "Epoch 5/20\n",
      "469/469 [==============================] - 65s 139ms/step - loss: 0.8658 - accuracy: 0.7126\n",
      "Epoch 6/20\n",
      "469/469 [==============================] - 59s 126ms/step - loss: 0.7033 - accuracy: 0.7720\n",
      "Epoch 7/20\n",
      "469/469 [==============================] - 46s 97ms/step - loss: 0.5738 - accuracy: 0.8180\n",
      "Epoch 8/20\n",
      "469/469 [==============================] - 47s 100ms/step - loss: 0.4754 - accuracy: 0.8503\n",
      "Epoch 9/20\n",
      "469/469 [==============================] - 49s 103ms/step - loss: 0.3988 - accuracy: 0.8748\n",
      "Epoch 10/20\n",
      "469/469 [==============================] - 44s 95ms/step - loss: 0.3376 - accuracy: 0.8950\n",
      "Epoch 11/20\n",
      "469/469 [==============================] - 47s 100ms/step - loss: 0.2925 - accuracy: 0.9090\n",
      "Epoch 12/20\n",
      "469/469 [==============================] - 45s 95ms/step - loss: 0.2582 - accuracy: 0.9203\n",
      "Epoch 13/20\n",
      "469/469 [==============================] - 42s 89ms/step - loss: 0.2288 - accuracy: 0.9309\n",
      "Epoch 14/20\n",
      "469/469 [==============================] - 41s 88ms/step - loss: 0.2065 - accuracy: 0.9371\n",
      "Epoch 15/20\n",
      "469/469 [==============================] - 44s 93ms/step - loss: 0.1902 - accuracy: 0.9427\n",
      "Epoch 16/20\n",
      "469/469 [==============================] - 45s 95ms/step - loss: 0.1764 - accuracy: 0.9461\n",
      "Epoch 17/20\n",
      "469/469 [==============================] - 45s 95ms/step - loss: 0.1630 - accuracy: 0.9506\n",
      "Epoch 18/20\n",
      "469/469 [==============================] - 45s 97ms/step - loss: 0.1528 - accuracy: 0.9542\n",
      "Epoch 19/20\n",
      "469/469 [==============================] - 44s 94ms/step - loss: 0.1434 - accuracy: 0.9567\n",
      "Epoch 20/20\n",
      "469/469 [==============================] - 44s 94ms/step - loss: 0.1351 - accuracy: 0.9590\n",
      "79/79 [==============================] - 3s 34ms/step - loss: 0.1245 - accuracy: 0.9622\n",
      "\n",
      "Test accuracy: 96.2%\n"
     ]
    }
   ],
   "source": [
    "from __future__ import absolute_import\n",
    "from __future__ import division\n",
    "from __future__ import print_function\n",
    "\n",
    "import numpy as np\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.layers import Dense, LSTM\n",
    "from tensorflow.keras.utils import to_categorical\n",
    "from tensorflow.keras.datasets import mnist\n",
    "\n",
    "# load mnist dataset\n",
    "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n",
    "\n",
    "# compute the number of labels\n",
    "num_labels = len(np.unique(y_train))\n",
    "\n",
    "# convert to one-hot vector\n",
    "y_train = to_categorical(y_train)\n",
    "y_test = to_categorical(y_test)\n",
    "\n",
    "# resize and normalize\n",
    "image_size = x_train.shape[1]\n",
    "x_train = np.reshape(x_train,[-1, image_size, image_size])\n",
    "x_test = np.reshape(x_test,[-1, image_size, image_size])\n",
    "x_train = x_train.astype('float32') / 255\n",
    "x_test = x_test.astype('float32') / 255\n",
    "\n",
    "# network parameters\n",
    "input_shape = (image_size, image_size)\n",
    "batch_size = 128\n",
    "units = 256\n",
    "\n",
    "# model is LSTM with 256 units, input is 28-dim vector 28 timesteps\n",
    "model = Sequential(name='LSTM_MNIST')\n",
    "model.add(LSTM(units=units,\n",
    "               input_shape=input_shape))\n",
    "model.add(Dense(num_labels, activation='softmax'))\n",
    "model.summary()\n",
    "\n",
    "# loss function for one-hot vector\n",
    "# accuracy is good metric for classification tasks\n",
    "model.compile(loss='categorical_crossentropy',\n",
    "              optimizer='sgd',\n",
    "              metrics=['accuracy'])\n",
    "# train the network\n",
    "model.fit(x_train, y_train, epochs=20, batch_size=batch_size)\n",
    "\n",
    "loss, acc = model.evaluate(x_test, y_test, batch_size=batch_size)\n",
    "print(\"\\nTest accuracy: %.1f%%\" % (100.0 * acc))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
